package fm.controller;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.baidu.ueditor.ActionEnter;
import fm.cache.ConfigCache;
import fm.exception.BizException;
import fm.listener.FileUploadProgressListener;
import fm.model.FileUploadStatus;
import fm.mongoService.UserService;
import fm.util.CommonUtils;
import fm.util.RequestUtils;
import fm.web.CurrentRequest;
import fm.web.MediaTypes;
import net.coobird.thumbnailator.Thumbnails;
import net.sf.ehcache.loader.CacheLoader;
import org.apache.commons.collections.MapUtils;
import org.apache.commons.fileupload.FileItem;
import org.apache.commons.fileupload.disk.DiskFileItemFactory;
import org.apache.commons.fileupload.servlet.ServletFileUpload;
import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.ModelMap;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.io.File;
import java.io.PrintWriter;
import java.text.DecimalFormat;
import java.util.*;

/**
 * Created by CM on 2014/8/6 0006.
 */
@Controller
@RequestMapping("/common/file/upload")
public class FileUploadController extends BaseController {
    private static final Logger LOGGER = LoggerFactory.getLogger(FileUploadController.class);
    @Autowired
    HttpSession session;
    //文件大小限制
    private static int MAX_UPLOAD_SIZE = 1024 * 1024 * 1024;
    //默认文件类型保存目录
    private static String SAVE_PATH = "Images";
    //文件上传地址
    private static final String UPLOAD_PATH = "/resources/upload";
    @Autowired
    private FileUploadProgressListener fileUploadProgressListener;
    @Autowired
    UserService userService;

    @ResponseBody
    @RequestMapping(value = "/config")
    public String index(String action, String callback) throws Exception {
        String rootPath = session.getServletContext().getRealPath("/");
        HttpServletRequest rq = CurrentRequest.getRequest();
        String res = new ActionEnter(rq, rootPath, action, callback).exec();
        return res;
    }

    /**
     * 文件上传，按照文件类型分类存储在不同路径
     *
     * @param request
     */
    @RequestMapping(value = "/ue/{fileType}")
    public void ueFileUpload(@PathVariable(value = "fileType") String fileType,
                             HttpServletResponse response, HttpServletRequest request) throws Exception {
        if (fileType != null && "".equals(fileType) == false) {
            SAVE_PATH = fileType;
        }
        Map modelMap = new HashMap();
        try {
            String absolutePath;
            String fileUri;
            DiskFileItemFactory factory = new DiskFileItemFactory();
            ServletFileUpload upload = new ServletFileUpload(factory);
            upload.setProgressListener(fileUploadProgressListener);
            upload.setFileSizeMax(MAX_UPLOAD_SIZE);

            List<FileItem> items = upload.parseRequest(request);
            FileItem file = null;
            if (items.size() <= 0) {
                throw new BizException("未找到要上传的文件，请选择文件");
            }
            for (FileItem item : items) {
                if (item.isFormField()) {
                } else {
                    //文件类型
                    file = item;
                }
            }
            if (file == null) {
                throw new BizException("未找到要上传的文件");
            } else {
                //获取文件名
                String originalName = file.getName();
                //根据时间生成新的随机文件名
                final String fileName = getSaveFileName(originalName);
                //存储路径，默认为Images
                String savePath = SAVE_PATH;
                //获取存储文件夹的绝对路径，不存在就创建一个新的文件夹
                final String realPath = getFolder(request, savePath);
                //返回路径：返回一个全局的路径
                absolutePath = realPath + "/" + fileName;
                //文件存储的相对路径
                fileUri = UPLOAD_PATH + "/" + SAVE_PATH + "/" + fileName;
                String fileUrl = fullPathBuilder(fileUri, request, response);
                file.write(new File(absolutePath));

                final FileItem tempFile = file;
                final String tempAbsolutePath = absolutePath;
                if (fileType.equals("images")) {
                    new Thread(new Runnable() {
                        @Override
                        public void run() {
                            try {
                                String thumbFileName = fileName.split(".")[0] + "-thmumb.jpg";

                                long size = tempFile.getSize();
                                double scale = 1.0d;
                                //大于200K的图片就要压缩 文件存储为JPG格式
                                if (size >= 200 * 1024) {
                                    scale = (200 * 1024f) / size;
                                }
                                String thumbAbsolutePath = realPath + "/" + thumbFileName;
                                Thumbnails.of(tempAbsolutePath).scale(scale).outputFormat("jpg").toFile(thumbAbsolutePath);
                                LOGGER.info("略缩图压缩完成:{}", thumbAbsolutePath);
                            } catch (Exception ex) {
                                LOGGER.error("压缩图片发生错误", ex.getMessage());
                            }
                        }
                    }).start();

                }


                LOGGER.info("【上传文件】文件保存绝对路径： {} \r\n 文件URL： {}", absolutePath, fileUrl);
                modelMap.put("url",fileUrl.indexOf(";jsessionid=")!=-1? fileUrl.split(";jsessionid=")[0]:fileUrl);
                modelMap.put("thumb-url",fileUrl.indexOf(";jsessionid=")!=-1? fileUrl.split(";jsessionid=")[0].split(".")[0] + "-thmumb.jpg":fileUrl);
                modelMap.put("title", "");
                modelMap.put("original", "");
            }
            this.success(modelMap);
        } catch (BizException ex) {
            LOGGER.error("occur error in file upload : {}", ex.getMessage());
            modelMap.put("state", "上传失败");
            this.failed(modelMap, ex);
        } catch (Exception ex) {
            LOGGER.error("occur error in file upload : {}", ex);
            modelMap.put("state", "上传失败");
            this.failed(modelMap, "serve error!");
        }
        String responseData = JSON.toJSONString(modelMap);
        LOGGER.debug("【上传文件-结束】文件信息:{}", responseData);
        PrintWriter out = response.getWriter();
        out.write(responseData);
        out.flush();
        out.close();
    }


    @ResponseBody
    @RequestMapping(value = "/{fileType}", produces = MediaTypes.JSON_UTF_8)
    public String fileUpload(HttpServletResponse response, HttpServletRequest request, ModelMap modelMap,
                             @PathVariable(value = "fileType") String fileType) throws Exception {
        if (fileType != null && "".equals(fileType) == false) {
            SAVE_PATH = fileType;
        }

        try {


            Map params = new HashMap();
            FileItem fileItem = null;
            String absolutePath;
            String fileUri;

            DiskFileItemFactory factory = new DiskFileItemFactory();
            ServletFileUpload upload = new ServletFileUpload(factory);
            upload.setProgressListener(fileUploadProgressListener);
            upload.setFileSizeMax(MAX_UPLOAD_SIZE);
            List<FileItem> items = upload.parseRequest(request);
            if (items.size() <= 0) {
                throw new BizException("未找到要上传的文件，请选择文件");
            }
            for (FileItem item : items) {
                if (item.isFormField()) {
                    //普通文本类型
                    params.put(item.getFieldName(), item.getString());
                } else {
                    //文件类型
                    fileItem = item;
                }
            }
            //放在循环之外，只获取最后一个文件
            if (fileItem == null) {
                throw new BizException("未找到要上传的文件");
            } else {
                //获取文件名
                String originalName = fileItem.getName();
                //根据时间生成新的随机文件名
                final String fileName = getSaveFileName(originalName);
                //存储路径，默认为Images
                String savePath = SAVE_PATH;
                //获取存储文件夹的绝对路径，不存在就创建一个新的文件夹
                final String realPath = getFolder(request, savePath);
                //返回路径：返回一个全局的路径
                absolutePath = realPath + "/" + fileName;
                //文件存储的相对路径
                fileUri = UPLOAD_PATH + "/" + SAVE_PATH + "/" + fileName;
                String fileUrl = fullPathBuilder(fileUri, request, response);
                //写出文件到硬盘
                fileItem.write(new File(absolutePath));


                final FileItem tempFile = fileItem;
                final String tempAbsolutePath = absolutePath;
                if (fileType.equals("image")) {
                    new Thread(new Runnable() {
                        @Override
                        public void run() {
                            try {
                                String thumbFileName = fileName.substring(0, fileName.lastIndexOf(".")) + "-thumb.jpg";

                                long size = tempFile.getSize();
                                double scale = 1.0d;
                                //大于200K的图片就要压缩 文件存储为JPG格式
                                if (size >= 200 * 1024) {
                                    scale = (200 * 1024f) / size;
                                }
                                String thumbAbsolutePath = realPath + "/" + thumbFileName;
                                Thumbnails.of(tempAbsolutePath).scale(scale).outputFormat("jpg").toFile(thumbAbsolutePath);
                                LOGGER.info("略缩图压缩完成:{}", thumbAbsolutePath);
                            } catch (Exception ex) {
                                LOGGER.error("压缩图片发生错误", ex);
                            }
                        }
                    }).start();
                }


                String remoteServer = ConfigCache.getConfig("FILE_UPLOAD_PROXY_SERVER");
                if (StringUtils.isNotEmpty(remoteServer) && remoteServer.indexOf(request.getRemoteHost()) == -1) {
                    Map<String, String> fileParam = new HashMap<>();
                    fileParam.put("proxFile", absolutePath);
                    String url = ConfigCache.getConfig("FILE_UPLOAD_PROXY_SERVER") + request.getRequestURI();
                    String result = RequestUtils.postFile(url, MapUtils.EMPTY_MAP, fileParam);
                    modelMap.put("proxResult", result);
                }
                LOGGER.info("【上传文件】文件保存绝对路径： {} \r\n 文件URL： {}", absolutePath, fileUrl);
                modelMap.put("url", fileUrl);
                modelMap.put("title", "");
                modelMap.put("original", "");
                this.success(modelMap);
            }
        } catch (BizException ex) {
            LOGGER.error("occur error in file upload : {}", ex.getMessage());
            modelMap.put("state", "上传失败");
            this.failed(modelMap, ex);
        } catch (Exception ex) {
            LOGGER.error("occur error in file upload : {}", ex);
            modelMap.put("state", "上传失败");
            this.failed(modelMap, "serve error!");
        }
        String responseData = JSON.toJSONString(modelMap);
        LOGGER.debug("【上传文件-结束】文件信息:{}", responseData);
        return responseData;
    }


    @ResponseBody
    @RequestMapping(value = "/progress", method = RequestMethod.POST, produces = MediaTypes.JSON_UTF_8)
    public String getProgress(ModelMap modelMap) {
        FileUploadStatus status = (FileUploadStatus) session.getAttribute(FileUploadProgressListener.FILE_UP_LOAD_STATUS);
        if (CommonUtils.isEmpty(status)) {
            this.failed(modelMap, "没有文件上传状态!");
        } else {
            this.success(modelMap);
            modelMap.put("progress", new DecimalFormat("#.00").
                    format((double) status.getReadLength() * 100 / (double) status.getFileLength()));
            modelMap.put("index", status.getCurrentItemIndex());

        }
        return JSON.toJSONString(modelMap);
    }

    /**
     * 获取文件的http完整链接
     *
     * @param saveUri
     * @param request
     * @param response
     * @return
     */
    public String fullPathBuilder(String saveUri, HttpServletRequest request, HttpServletResponse response) {
        String contextPath = request.getContextPath();

        StringBuffer buffer = new StringBuffer();
        buffer.append(request.getScheme()).append("://").append(request.getServerName()).append(":")
                .append(request.getServerPort()).append(contextPath).append(saveUri);
        if (LOGGER.isDebugEnabled()) {
            LOGGER.debug("file save full path before encode :{}", buffer.toString());
        }
        return response.encodeURL(buffer.toString());
    }

    /**
     * 依据原始文件名生成新文件名
     *
     * @return
     */
    public String getSaveFileName(String fileName) throws BizException {

        return System.currentTimeMillis() + this.getFileExt(fileName);
    }

    /**
     * 根据字符串创建本地目录 并建立子目录返回
     *
     * @param path
     * @return
     */
    public String getFolder(HttpServletRequest request, String path) {
        File dir = new File(this.getPhysicalPath(request, path));
        if (!dir.exists()) {
            dir.mkdirs();
        }

        return dir.getAbsolutePath();
    }

    /**
     * 根据传入的虚拟路径获取物理路径
     *
     * @param path
     * @return
     */
    private String getPhysicalPath(HttpServletRequest request, String path) {
        String realPath = this.UPLOAD_PATH;
        if (this.UPLOAD_PATH == null || this.UPLOAD_PATH.startsWith("/"))
            realPath = request.getSession().getServletContext().getRealPath(this.UPLOAD_PATH);
        return realPath + "/" + path;
    }


    /**
     * 获取文件扩展名
     *
     * @return string
     */
    private String getFileExt(String fileName) throws BizException {
        String ext = fileName.substring(fileName.lastIndexOf("."));
        if (StringUtils.isBlank(ext)) {
            throw new BizException("服务器不支持上传此文件类型!");
        }
        return ext.toLowerCase();
    }
}
